package com.netty.handler;

import com.netty.CIMSocketRequestHandler;
import com.netty.constant.Constants;
import com.netty.model.proto.MessageBodyProto;
import com.netty.utils.Md5Utils;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.Objects;

/**
 * @author zl
 * @version 1.0
 * @date 2021/9/15 9:32
 */
@ChannelHandler.Sharable
public class ImSocketServerHandler extends ChannelInboundHandlerAdapter {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private CIMSocketRequestHandler cimRequestHandler;

    private ChannelGroup channelGroup;

    public ImSocketServerHandler(CIMSocketRequestHandler cimRequestHandler, ChannelGroup channelGroup) {
        this.cimRequestHandler = cimRequestHandler;
        this.channelGroup = channelGroup;
    }


    @Override
    public void channelRegistered(ChannelHandlerContext ctx) {
        logger.debug(">>>>>>>>>>>>>>>>>>>>>>channel注册了<<<<<<<<<<<<<<<<<<<<<<");
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) {
        logger.debug(">>>>>>>>>>>>>>>>>>>>>>channel移除了<<<<<<<<<<<<<<<<<<<<<<");
        if (ctx.channel().attr(Constants.SessionConfig.UID) == null) {
            return;
        }
        Constants.executorService.submit(new Runnable() {
            @Override
            public void run() {
                ctx.close();
                cimRequestHandler.channelUnregistered(ctx.channel());
            }
        });
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //获取服务端ip地址以及端口号
        InetSocketAddress sSocket = (InetSocketAddress)ctx.channel().localAddress();
        ctx.channel().attr(Constants.SessionConfig.SIPS).set(sSocket.getAddress().getHostAddress());
        ctx.channel().attr(Constants.SessionConfig.SPORT).set( sSocket.getPort());
        //获取服务端ip地址以及端口号
        //获取客户端ip地址以及端口号
        InetSocketAddress uSocket = (InetSocketAddress)ctx.channel().remoteAddress();
        ctx.channel().attr(Constants.SessionConfig.UIPS).set(uSocket.getAddress().getHostAddress());
        ctx.channel().attr(Constants.SessionConfig.UPORT).set( uSocket.getPort());
        //获取客户端ip地址以及端口号
        ctx.channel().attr(Constants.SessionConfig.ID).set(Md5Utils.shortMd5(ctx.channel().id().asLongText()));
        ctx.channel().attr(Constants.SessionConfig.CHANNEL).set(Constants.ImserverConfig.SOCKET);
        Constants.executorService.submit(new Runnable() {
            @Override
            public void run() {
                cimRequestHandler.channelActive(ctx.channel());
            }
        });
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object body) throws Exception {
        Constants.executorService.submit(new Runnable() {
            @Override
            public void run() {
                if (body instanceof MessageBodyProto.Model) {
                    MessageBodyProto.Model model = (MessageBodyProto.Model) body;
                    switch (model.getType()){
                        case Constants.CmdType.ONLINE:
                            ctx.channel().attr(Constants.SessionConfig.UID).set(model.getKey());
                            MessageBodyProto.Model.Builder message = MessageBodyProto.Model.newBuilder();
                            message.setType(Constants.CmdType.MESSAGE);
                            message.setTimestamp(System.currentTimeMillis());
                            if(Objects.nonNull(channelGroup)){
                                channelGroup.add(ctx.channel());
                            }
                            ctx.channel().writeAndFlush(message);
                            break;
                        case Constants.CmdType.OFFLINE:
                            channelUnregistered(ctx);
                            break;
                        case Constants.CmdType.PING:
                            MessageBodyProto.Model.Builder reply = MessageBodyProto.Model.newBuilder();
                            reply.setType(Constants.CmdType.PONG);
                            reply.setTimestamp(System.currentTimeMillis());
                            ctx.channel().writeAndFlush(reply);
                            break;
                        default:
                            Constants.executorService.submit(new Runnable() {
                                @Override
                                public void run() {
                                    cimRequestHandler.channelRead0(ctx, model);
                                }
                            });
                    }
                }
            }
        });
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (!(evt instanceof IdleStateEvent)) {
            return;
        }
        IdleStateEvent idleEvent = (IdleStateEvent) evt;
        String uid = ctx.channel().attr(Constants.SessionConfig.UID).get();
        /*
         * 关闭未认证的连接
         */
        if (idleEvent.state() == IdleState.WRITER_IDLE && uid == null) {
            ctx.close();
            return;
        }
        /*
         * 如果心跳请求发出60秒内没收到响应，则关闭连接
         */
        if (idleEvent.state() == IdleState.READER_IDLE) {
            ctx.close();
            logger.info("{} pong timeout.", ctx.channel());
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.debug(">>>>>>>>>>>>>>>>>>>>>>channel 捕获到异常了，关闭了{}<<<<<<<<<<<<<<<<<<<<<<", cause);
        super.exceptionCaught(ctx, cause);
        cimRequestHandler.channelUnregistered(ctx.channel());
    }


}
